﻿/*
 * Copyright © 2011 Tristan Reeves, treeves@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Linq;

namespace Sprinkles.Secondary
{
    public abstract class BaseArguments<T>
    {
        private readonly Deployment<T> m_Owner;
        private readonly Arg[] m_ArgInfo;
        private readonly Arg m_MyArgInfo;

        protected Deployment<T> SetArgValue(object arg)
        {
            m_MyArgInfo.Value = arg;
            m_Owner.AddConstructorArg(m_MyArgInfo);
            return m_Owner;
        }
        protected Arguments<B, T> SetArgValue<A, B>(Arguments<A, B, T> orginal, object arg)
        {
            m_MyArgInfo.Value = arg;
            m_Owner.AddConstructorArg(m_MyArgInfo);
            return new Arguments<B, T>(m_Owner, m_ArgInfo);
        }
        protected Arguments<B, C, T> SetArgValue<A, B, C>(Arguments<A, B, C, T> orginal, object arg)
        {
            m_MyArgInfo.Value = arg;
            m_Owner.AddConstructorArg(m_MyArgInfo);
            return new Arguments<B, C, T>(m_Owner, m_ArgInfo);
        }
        protected Arguments<B, C, D, T> SetArgValue<A, B, C, D>(Arguments<A, B, C, D, T> orginal, object arg)
        {
            m_MyArgInfo.Value = arg;
            m_Owner.AddConstructorArg(m_MyArgInfo);
            return new Arguments<B, C, D, T>(m_Owner, m_ArgInfo);
        }
        protected Arguments<B, C, D, E, T> SetArgValue<A, B, C, D, E>(Arguments<A, B, C, D, E, T> orginal, object arg)
        {
            m_MyArgInfo.Value = arg;
            m_Owner.AddConstructorArg(m_MyArgInfo);
            return new Arguments<B, C, D, E, T>(m_Owner, m_ArgInfo);
        }

        protected BaseArguments(Deployment<T> owner, Arg[] argInfo)
        {
            m_Owner = owner;
            m_ArgInfo = argInfo.Skip(1).ToArray();
            m_MyArgInfo = argInfo.First();
        }
    }

    public class Arguments<A, T> : BaseArguments<T>
    {
        public Deployment<T> SetLiteral(A arg)
        {
            return SetArgValue(arg);
        }
        public Deployment<T> SetUntypedObject(object arg)
        {
            return SetArgValue(arg);
        }
        public Deployment<T> SetExpression(string expression)
        {
            var arg = new SprinklesExpressionHolder(expression);
            return SetArgValue(arg);
        }
        public Deployment<T> SetReference(string reference)
        {
            var arg = new SprinklesRuntimeObjectReference(reference);
            return SetArgValue(arg);
        }
        public Deployment<T> SetReference<TDerived>(FutureDeployedObject<TDerived> futureReference) where TDerived : A
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(futureReference);
        }
        public Deployment<T> SetReferenceUntyped(IFutureDeployedObject futureReference)
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(futureReference);
        }
        public Deployment<T> SetAuto()
        {
            return SetArgValue(new AutoReference());
        }
        public Deployment<T> SetPlaceholder(string arg)
        {
            return SetArgValue(arg);
        }
        public Arguments(Deployment<T> owner, Arg[] argInfo) : base(owner, argInfo) { }
    }
    public class Arguments<A, B, T> : BaseArguments<T>
    {
        public Arguments<B, T> SetLiteral(A arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments<B, T> SetUntypedObject(object arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments<B, T> SetExpression(string expression)
        {
            var arg = new SprinklesExpressionHolder(expression);
            return SetArgValue(this, arg);
        }
        public Arguments<B, T> SetReference(string reference)
        {
            var arg = new SprinklesRuntimeObjectReference(reference);
            return SetArgValue(this, arg);
        }
        public Arguments<B, T> SetReference<TDerived>(FutureDeployedObject<TDerived> futureReference) where TDerived : A
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(this, futureReference);
        }
        public Arguments<B, T> SetReferenceUntyped(IFutureDeployedObject futureReference)
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(this, futureReference);
        }
        public Arguments<B, T> SetAuto()
        {
            return SetArgValue(this, new AutoReference());
        }
        public Arguments<B, T> SetPlaceholder(string arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments(Deployment<T> owner, Arg[] argInfo) : base(owner, argInfo) { }
    }
    public class Arguments<A, B, C, T> : BaseArguments<T>
    {
        public Arguments<B, C, T> SetLiteral(A arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, T> SetUntypedObject(object arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, T> SetExpression(string expression)
        {
            var arg = new SprinklesExpressionHolder(expression);
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, T> SetReference(string reference)
        {
            var arg = new SprinklesRuntimeObjectReference(reference);
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, T> SetReference<TDerived>(FutureDeployedObject<TDerived> futureReference) where TDerived : A
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(this, futureReference);
        }
        public Arguments<B, C, T> SetReferenceUntyped(IFutureDeployedObject futureReference)
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(this, futureReference);
        }
        public Arguments<B, C, T> SetAuto()
        {
            return SetArgValue(this, new AutoReference());
        }
        public Arguments<B, C, T> SetPlaceholder(string arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments(Deployment<T> owner, Arg[] argInfo) : base(owner, argInfo) { }
    }
    public class Arguments<A, B, C, D, T> : BaseArguments<T>
    {
        public Arguments<B, C, D, T> SetLiteral(A arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, D, T> SetUntypedObject(object arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, D, T> SetExpression(string expression)
        {
            var arg = new SprinklesExpressionHolder(expression);
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, D, T> SetReference(string reference)
        {
            var arg = new SprinklesRuntimeObjectReference(reference);
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, D, T> SetReference<TDerived>(FutureDeployedObject<TDerived> futureReference) where TDerived : A
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(this, futureReference);
        }
        public Arguments<B, C, D, T> SetReferenceUntyped(IFutureDeployedObject futureReference)
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(this, futureReference);
        }
        public Arguments<B, C, D, T> SetAuto()
        {
            return SetArgValue(this, new AutoReference());
        }
        public Arguments<B, C, D, T> SetPlaceholder(string arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments(Deployment<T> owner, Arg[] argInfo) : base(owner, argInfo) { }
    }
    public class Arguments<A, B, C, D, E, T> : BaseArguments<T>
    {
        public Arguments<B, C, D, E, T> SetLiteral(A arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, D, E, T> SetUntypedObject(object arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, D, E, T> SetExpression(string expression)
        {
            var arg = new SprinklesExpressionHolder(expression);
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, D, E, T> SetReference(string reference)
        {
            var arg = new SprinklesRuntimeObjectReference(reference);
            return SetArgValue(this, arg);
        }
        public Arguments<B, C, D, E, T> SetReference<TDerived>(FutureDeployedObject<TDerived> futureReference) where TDerived : A
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(this, futureReference);
        }
        public Arguments<B, C, D, E, T> SetReferenceUntyped(IFutureDeployedObject futureReference)
        {
            if (futureReference == null) throw new ArgumentNullException("futureReference");
            return SetArgValue(this, futureReference);
        }
        public Arguments<B, C, D, E, T> SetAuto()
        {
            return SetArgValue(this, new AutoReference());
        }
        public Arguments<B, C, D, E, T> SetPlaceholder(string arg)
        {
            return SetArgValue(this, arg);
        }
        public Arguments(Deployment<T> owner, Arg[] argInfo) : base(owner, argInfo) { }
    }
}
